package gov.pbc.nn.core.dao;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.Session;

import com.bstek.bdf2.core.context.ContextHolder;
import com.bstek.bdf2.core.orm.hibernate.HibernateDao;
import com.bstek.bdf2.core.orm.hibernate.ISessionCallback;
import com.bstek.dorado.data.provider.Criteria;
import com.bstek.dorado.data.provider.Junction;
import com.bstek.dorado.data.provider.filter.SingleValueFilterCriterion;
import com.bstek.dorado.util.proxy.ProxyBeanUtils;

import gov.pbc.nn.core.entity.BaseEntity;

/**
 * Hibernate操作的基础类，所有的支持Hibernate操作的业务类（非BDF2底层类）都可以继承该类
 * 
 * @author Louis
 * 
 */
public abstract class AbstractHibernateDao extends HibernateDao {

	/**
	 * 根据ID加载某个实体对象
	 * 
	 * @param id
	 * @param clazz
	 * @return
	 */
	public <T> T get(final Serializable id, final Class<T> clazz) {
		return (T) this.doInHibernateSession(new ISessionCallback<T>() {

			@Override
			public T doInSession(Session session) {
				return (T) session.get(clazz, id);
			}

		});
	}

	/**
	 * 保存一个实体对象
	 * 
	 * @param entity
	 * @return
	 */
	public Serializable save(final BaseEntity entity) {
		return this.doInHibernateSession(new ISessionCallback<Serializable>() {

			@Override
			public Serializable doInSession(Session session) {
				entity.setCreateUser(ContextHolder.getLoginUserName());
				entity.setCreateUserCname(ContextHolder.getLoginUser().getCname());
				entity.setCreateDate(new Date());
				return session.save(entity);
			}

		});
	}

	/**
	 * 更新一个实体对象
	 * 
	 * @param entity
	 */
	public void update(final BaseEntity entity) {
		this.doInHibernateSession(new ISessionCallback<Void>() {

			@Override
			public Void doInSession(Session session) {
				entity.setUpdateDate(new Date());
				entity.setUpdateUser(ContextHolder.getLoginUserName());
				session.update(entity);
				return null;
			}

		});
	}

	/**
	 * 删除一个实体对象。所谓的删除并非真的从数据库表中删除一条记录，仅仅是将其标记为无效而已。
	 * 
	 * @param entity
	 */
	public void delete(final BaseEntity entity) {
		this.doInHibernateSession(new ISessionCallback<Void>() {

			@Override
			public Void doInSession(Session session) {
				entity.setUpdateDate(new Date());
				entity.setUpdateUser(ContextHolder.getLoginUserName());
				entity.setUpdateUserCname(ContextHolder.getLoginUser().getCname());
				entity.setInvalid(true);
				session.update(entity);
				return null;
			}

		});
	}

	/**
	 * 根据ID和全限定类名删除一个实体对象。所谓的删除并非真的从数据库表中删除一条记录，仅仅是将其标记为无效而已。
	 * 
	 * @param id
	 * @param className
	 *            该类必须是gov.pbc.nn.core.entity.BaseEntity的子类
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public void delete(final String id, String className) throws Exception {
		final Class<?> clazz = Class.forName(className);
		if (BaseEntity.class.isAssignableFrom(clazz)) {
			this.delete(id, (Class<? extends BaseEntity>) clazz);
		}
	}

	/**
	 * 根据ID和Class删除一个实体对象。所谓的删除并非真的从数据库表中删除一条记录，仅仅是将其标记为无效而已。
	 * 
	 * @param id
	 * @param clazz
	 *            Class所表示的类必须是gov.pbc.nn.core.entity.BaseEntity的子类
	 */
	public void delete(final String id, final Class<? extends BaseEntity> clazz) {
		this.doInHibernateSession(new ISessionCallback<Void>() {

			@Override
			public Void doInSession(Session session) {
				Class<?> type = clazz;
				if (ProxyBeanUtils.isProxy(clazz)) {
					type = ProxyBeanUtils.getProxyTargetType(clazz);
				}
				BaseEntity entity = (BaseEntity) session.get(type, id);
				if (entity != null) {
					entity.setUpdateDate(new Date());
					entity.setUpdateUser(ContextHolder.getLoginUserName());
					entity.setUpdateUserCname(ContextHolder.getLoginUser().getCname());
					entity.setInvalid(true);
					session.update(entity);
				}
				return null;
			}

		});
	}

	public int executeUpdate(final String hql, final Map<String, Object> params) {
		return this.doInHibernateSession(new ISessionCallback<Integer>() {

			@Override
			public Integer doInSession(Session session) {
				if (params == null || params.isEmpty()) {
					return session.createQuery(hql).executeUpdate();
				}
				return session.createQuery(hql).setProperties(params).executeUpdate();
			}

		});
	}

	/**
	 * 判断过滤条件中是否存在某属性
	 * 
	 * @param propery
	 * @param criteria
	 * @return
	 */
	public boolean containsProperty(String propery, Criteria criteria) {
		if (criteria == null) {
			return false;
		}
		for (com.bstek.dorado.data.provider.Criterion c : criteria.getCriterions()) {
			if (c instanceof SingleValueFilterCriterion) {
				SingleValueFilterCriterion fc = (SingleValueFilterCriterion) c;
				if (StringUtils.equals(propery, fc.getProperty())) {
					return true;
				}
			}
			if (c instanceof Junction) {
				Junction jun = (Junction) c;
				Collection<com.bstek.dorado.data.provider.Criterion> subCriterions = jun.getCriterions();
				if (subCriterions != null) {
					return buildCriterions(propery, subCriterions);
				}
			}
		}
		return false;
	}

	private boolean buildCriterions(String propery, Collection<com.bstek.dorado.data.provider.Criterion> criterions) {
		for (com.bstek.dorado.data.provider.Criterion c : criterions) {
			if (c instanceof SingleValueFilterCriterion) {
				SingleValueFilterCriterion fc = (SingleValueFilterCriterion) c;
				if (StringUtils.equals(propery, fc.getProperty())) {
					return true;
				}
			}
			if (c instanceof Junction) {
				Junction jun = (Junction) c;
				Collection<com.bstek.dorado.data.provider.Criterion> subCriterions = jun.getCriterions();
				if (subCriterions != null) {
					return buildCriterions(propery, subCriterions);
				}
			}
		}
		return false;
	}
}
